/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-09-07     UAVI02       the first version
 */
#include "spl0601.h"
#include <drv_spi.h>
#include <rtthread.h>
#include <rtdbg.h>

#define DBG_TAG "SPL0601"
#define DBG_TVL DBG_LOG
#define SPL0601_DEVICE_NAME "spl0601"

static const uint32_t scale[8] = {524288, 1572864, 3670016, 7864320, 253952, 516096, 1040384, 2088960};

static rt_err_t spl0601_spi_init(spl0601_device_t *dev)
{
    dev->spi_dev = (struct rt_spi_device *)rt_device_find(SPL0601_DEVICE_NAME);
    if (dev->spi_dev != RT_NULL)
    {
        dev->spi_dev->config.max_hz = 10000000;
        dev->spi_dev->config.data_width = 8;
        dev->spi_dev->config.mode = RT_SPI_MODE_3 | RT_SPI_MSB;
        LOG_D("%s spi init ok!", SPL0601_DEVICE_NAME);
        return RT_EOK;
    }
    else
    {
        LOG_E("init failed! can't find %s spi!", SPL0601_DEVICE_NAME);
        return -RT_ERROR;
    }
}
static rt_err_t spl0601_spi_read_reg(spl0601_device_t *dev, uint8_t reg_addr, uint8_t data)
{
    return rt_spi_send_then_recv(dev, &reg_addr, 1, &data, 1);
}
static rt_err_t spl0601_spi_read_regs(spl0601_device_t *dev, uint8_t reg_addr, uint8_t *data, uint8_t len)
{
    return rt_spi_send_then_recv(dev, &reg_addr, 1, data, len);
}
static rt_err_t spl0601_spi_write_reg(spl0601_device_t *dev, uint8_t reg_addr, uint8_t data)
{
    return rt_spi_send_then_recv(dev, &reg_addr, 1, &data, 1);
}
static rt_err_t spl0601_spi_write_regs(spl0601_device_t *dev, uint8_t reg_addr, uint8_t *data, uint8_t len)
{
    return rt_spi_send_then_send(dev, &reg_addr, 1, data, len);
}

void spl0601_temperature_rateset(spl0601_device_t *dev, uint8_t samplerate, uint8_t oversample)
{
    dev->kT = scale[oversample];
    uint8_t temp = 0;
    spl0601_spi_write_reg(dev, SPL06_TEMPERATURE_CFG_REG, samplerate << 4 | oversample | 0x80);

    if (oversample > SPL06_OVERSAMP_8)
    {
        spl0601_spi_read_reg(dev, SPL06_INT_FIFO_CFG_REG, &temp);

        spl0601_spi_write_reg(dev, SPL06_INT_FIFO_CFG_REG, temp | 0x08);
    }
}
void spl0601_pressure_rateset(spl0601_device_t *dev, uint8_t samplerate, uint8_t oversample)
{
    dev->kP = scale[oversample];
    uint8_t temp = 0;
    spl0601_spi_write_reg(dev, SPL06_PRESSURE_CFG_REG, samplerate << 4 | oversample | 0x40);
    if (oversample > SPL06_OVERSAMP_8)
    {
        spl0601_spi_read_reg(dev, SPL06_INT_FIFO_CFG_REG, &temp);

        spl0601_spi_write_reg(dev, SPL06_INT_FIFO_CFG_REG, temp | 0x04);
    }
}
static void spl0601_get_calib_param(spl0601_device_t *dev)
{
    uint8_t rxbuff[SPL06_CALIB_COEFFICIENT_LENGTH];

    spl0601_spi_read_regs(dev, SPL06_COEFFICIENT_CALIB_REG, rxbuff, SPL06_CALIB_COEFFICIENT_LENGTH);
    dev->calib_param.c0 = (int16_t)rxbuff[0] << 4 | rxbuff[1] >> 4;
    dev->calib_param.c0 = (dev->calib_param.c0 & 0x0800) ? (dev->calib_param.c0 | 0xF000) : dev->calib_param.c0;

    dev->calib_param.c1 = (int16_t)(rxbuff[1] & 0x0F) << 8 | rxbuff[2];
    dev->calib_param.c1 = (dev->calib_param.c1 & 0x0800) ? (dev->calib_param.c1 | 0xF000) : dev->calib_param.c1;

    dev->calib_param.c00 = (int32_t)rxbuff[3] << 12 | (int32_t)rxbuff[4] << 4 | (int32_t)rxbuff[5] >> 4;
    dev->calib_param.c00 = (dev->calib_param.c00 & 0x080000) ? (dev->calib_param.c00 | 0xFFF00000) : dev->calib_param.c00;

    dev->calib_param.c10 = (int32_t)(rxbuff[5] & 0x0F) << 16 | (int32_t)rxbuff[6] << 8 | (int32_t)rxbuff[7];
    dev->calib_param.c10 = (dev->calib_param.c10 & 0x080000) ? (dev->calib_param.c10 | 0xFFF00000) : dev->calib_param.c10;

    dev->calib_param.c01 = (int16_t)rxbuff[8] << 8 | rxbuff[9];
    dev->calib_param.c11 = (int16_t)rxbuff[10] << 8 | rxbuff[11];
    dev->calib_param.c20 = (int16_t)rxbuff[12] << 8 | rxbuff[13];
    dev->calib_param.c21 = (int16_t)rxbuff[14] << 8 | rxbuff[15];
    dev->calib_param.c30 = (int16_t)rxbuff[16] << 8 | rxbuff[17];
}
static void spl0601_start_continuous(spl0601_device_t *dev)
{
    spl0601_spi_write_reg(dev, SPL06_MODE_CFG_REG, SPL06_CONTINUOUS_MODE);
}

static void spl0601_get_raw_temp(spl0601_device_t *dev)
{
    uint8_t buf[3];
    spl0601_spi_read_regs(dev, SPL06_TEMPERATURE_MSB_REG, buf, 3);
    dev->rawTemperature = (int32_t)buf[0] << 16 | (int32_t)buf[1] << 8 | (int32_t)buf[2];
    dev->rawTemperature = (dev->rawTemperature & 0x800000) ? (dev->rawTemperature | 0xFF000000) : dev->rawTemperature;
}
static void spl0601_get_raw_pressure(spl0601_device_t *dev)
{
    uint8_t buf[3];
    spl0601_spi_read_regs(dev, SPL06_PRESSURE_MSB_REG, buf, 3);
    dev->rawPressure = (int32_t)buf[0] << 16 | (int32_t)buf[1] << 8 | (int32_t)buf[2];
    dev->rawPressure = (dev->rawPressure & 0x800000) ? (dev->rawPressure | 0xFF000000) : dev->rawPressure;
}
static float spl0601_get_temperature(spl0601_device_t *dev)
{
    float fTCompensate;
    float fTsc;
    fTsc = dev->rawTemperature / (float)dev->kT;
    fTCompensate = dev->calib_param.c20 + dev->calib_param.c21 * fTsc;
    return fTCompensate;
}
static float spl0601_get_pressure(spl0601_device_t *dev)
{
    float fTsc, fPsc;
    float qua2, qua3;
    float fPCompensate;

    fTsc = dev->rawTemperature / (float)dev->kT;
    fPsc = dev->rawPressure / (float)dev->kP;
    qua2 = dev->calib_param.c10 + fPsc * (dev->calib_param.c20 + fPsc * dev->calib_param.c30);
    qua3 = fTsc * fPsc * (dev->calib_param.c11 + fPsc * dev->calib_param.c21);

    fPCompensate = dev->calib_param.c00 + fPsc * qua2 + fTsc * dev->calib_param.c01 + qua3;
    return fPCompensate;
}
static float spl0601_get_altitude(spl0601_device_t *dev)
{
    float altitude;
    float Pr, Tr;

    spl0601_get_raw_temp(dev);
    spl0601_get_raw_pressure(dev);

    Tr = spl0601_get_temperature(dev);
    Pr = spl0601_get_pressure(dev);

    altitude = 4433000.f * (1.0f - powf((Pr / 101325.0f), 0.190295f));

    return altitude;
}
static rt_err_t spl0601_init(spl0601_device_t *dev)
{
    rt_err_t res = RT_EOK;
    res = spl0601_spi_init(dev);
    dev->rawPressure = 0;
    dev->rawTemperature = 0;
    spl0601_spi_read_reg(dev,SPL06_CHIP_ID_REG,dev->dev_id);
    if(dev->dev_id != SPL06_DEFAULT_CHIP_ID)
    {
        LOG_E("spl0601 read id error, rx:0x%x, expect:0x%x", dev->dev_id, SPL06_DEFAULT_CHIP_ID);
        return -RT_ERROR;
    }
    spl0601_get_calib_param(dev);
    spl0601_pressure_rateset(dev, SPL06_MWASURE_32, SPL06_OVERSAMP_8);
    spl0601_temperature_rateset(dev, SPL06_MWASURE_32, SPL06_OVERSAMP_8);
    // spl0601_start_continuous(dev);

    return res;
}

static spl0601_device_t *_spl0601_init(struct rt_sensor_intf *intf)
{
    spl0601_device_t *spl0601 = RT_NULL;
    spl0601 = rt_malloc(sizeof(spl0601_device_t));
    if (spl0601 == RT_NULL)
        return RT_NULL;
    spl0601_init(spl0601);
    return spl0601;
}

static rt_size_t spl0601_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    spl0601_device_t *spl0601 = sensor->parent.user_data;
    struct rt_sensor_data *data = (struct rt_sensor_data *)buf;

    if (sensor->info.type == RT_SENSOR_CLASS_TEMP)
    {
        float temp;
        spl0601_get_raw_temp(spl0601);
        temp = spl0601_get_temperature(spl0601);
        data->type = RT_SENSOR_CLASS_TEMP;
        data->data.temp = (int32_t)temp;
        data->timestamp = rt_sensor_get_ts();
        // rt_kprintf("temp:%d\t", dev->i32rawTemperature);
        // rt_kprintf("i32kt:%d\n", dev->i32kT);
    }
    else if (sensor->info.type == RT_SENSOR_CLASS_BARO)
    {
        float pressure;
        spl0601_get_raw_pressure(spl0601);
        pressure = spl0601_get_pressure(spl0601);
        data->type = RT_SENSOR_CLASS_BARO;
        data->data.baro = (int32_t)pressure;
        data->timestamp = rt_sensor_get_ts();
        // rt_kprintf("press:%d\t", dev->i32rawPressure);
        // rt_kprintf("i32kp:%d\n", dev->i32kP);

    }
    // else if (sensor->info.type == RT_SENSOR_CLASS_BARO)
    // {
    //     float altitude;
    //     spl0601_get_raw_pressure(spl0601);
    //     altitude = spl0601_get_altitude(spl0601);
    //     data->type = RT_SENSOR_CLASS_BARO;
    //     data->data.baro = (rt_uint32_t)altitude;
    //     data->timestamp = rt_sensor_get_ts();
    // }
    return 1;
}
static void spl0601_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    spl0601_device_t *spl0601 = sensor->parent.user_data;

    switch (cmd)
    {
    case RT_SENSOR_CTRL_SET_ODR:
        if (sensor->info.type == RT_SENSOR_CLASS_TEMP)
        {
            spl0601_temperature_rateset(spl0601, SPL06_MWASURE_32, (rt_uint32_t)args);
        }
        if (sensor->info.type == RT_SENSOR_CLASS_BARO)
        {
            spl0601_pressure_rateset(spl0601, SPL06_MWASURE_32, (rt_uint32_t)args);
        }
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        spl0601_start_continuous(spl0601);
        break;
    default:
        break;
    }
}
static struct rt_sensor_ops spl0601_ops =
    {
        spl0601_fetch_data,
        spl0601_control};

int rt_hw_spl0601_init(const char *name, struct rt_sensor_config *cfg)
{

    rt_uint8_t result = RT_EOK;
    rt_sensor_t sensor_baro = RT_NULL, sensor_temp = RT_NULL;
    spl0601_device_t *spl0601 = _spl0601_init(&cfg->intf);

    if (spl0601 == RT_NULL)
    {
        LOG_E("_spl0601 init err!");
        return -RT_ERROR;
    }
    sensor_temp = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (sensor_temp == RT_NULL)
        return -RT_ERROR;

    sensor_temp->info.type = RT_SENSOR_CLASS_TEMP;
    sensor_temp->info.vendor = RT_SENSOR_VENDOR_STM;
    sensor_temp->info.model = "spl0601_temp";
    sensor_temp->info.unit = RT_SENSOR_UNIT_DCELSIUS;
    sensor_temp->info.intf_type = RT_SENSOR_INTF_SPI;
    sensor_temp->info.range_max = 120;
    sensor_temp->info.range_min = -40;
    sensor_temp->info.period_min = 80;
    sensor_temp->info.fifo_max = 0;

    rt_memcpy(&sensor_temp->config, cfg, sizeof(struct rt_sensor_config));
    sensor_temp->ops = &spl0601_ops;
    result = rt_hw_sensor_register(sensor_temp, name, RT_DEVICE_FLAG_RDWR, spl0601);

    sensor_baro = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (result != RT_EOK)
    {
        LOG_E("device register err code: %d", result);
        rt_free(sensor_temp);
        return -RT_ERROR;
    }
    LOG_I("spl0601 temp sensor init success");

    if (sensor_baro == RT_NULL)
        return -RT_ERROR;

    sensor_baro->info.type = RT_SENSOR_CLASS_BARO;
    sensor_baro->info.vendor = RT_SENSOR_VENDOR_STM;
    sensor_baro->info.model = "spl0601_baro";
    sensor_baro->info.unit = RT_SENSOR_UNIT_PA;
    sensor_baro->info.intf_type = RT_SENSOR_INTF_SPI;
    sensor_baro->info.range_max = 1000;
    sensor_baro->info.range_min = 0;
    sensor_baro->info.period_min = 80;
    sensor_baro->info.fifo_max = 0;

    rt_memcpy(&sensor_baro->config, cfg, sizeof(struct rt_sensor_config));
    sensor_baro->ops = &spl0601_ops;

    result = rt_hw_sensor_register(sensor_baro, name, RT_DEVICE_FLAG_RDWR, spl0601);

    if (result != RT_EOK)
    {
        LOG_E("device register err code: %d", result);
        rt_free(sensor_baro);
        return -RT_ERROR;
    }
    LOG_I("spl0601 baro sensor init success");
    return RT_EOK;
}
int rt_hw_spl0601_entry(void)
{
    struct rt_sensor_config cfg;

    rt_hw_spl0601_init("spl", &cfg);
}
ZINO_DEVICE_EXPORT(rt_hw_spl0601_entry);

void rt_hw_spl0601_data(void)
{
    rt_device_t spl_baro = RT_NULL, spl_temp = RT_NULL;
    struct rt_sensor_data spl0601_temp, spl0601_baro;

    spl_baro = rt_device_find("baro_spl");
    if (spl_baro == RT_NULL)
    {
        rt_kprintf("find baro_spl failed\n");
        return;
    }
    spl_temp = rt_device_find("temp_spl");
    if (spl_temp == RT_NULL)
    {
        rt_kprintf("find baro_spl failed\n");
        return;
    }
    rt_device_open(spl_baro, RT_DEVICE_FLAG_RDWR);
    rt_device_open(spl_temp, RT_DEVICE_FLAG_RDWR);
    rt_device_control(spl_baro, RT_SENSOR_CTRL_SET_ODR, SPL06_OVERSAMP_8);
    rt_device_control(spl_temp, RT_SENSOR_CTRL_SET_ODR, SPL06_OVERSAMP_8);
    while (1)
    {
        rt_device_read(spl_baro, 0, &spl0601_baro, 1);
        rt_device_read(spl_temp, 0, &spl0601_temp, 1);
        rt_kprintf("dev_temp:%5d, dev_baro:%5d, timestamp:%5d\n", spl0601_temp.data.temp, spl0601_baro.data.baro, spl0601_temp.timestamp);
        rt_thread_mdelay(50);
    }
}
